#pragma once

#include <map>
#include <ctime>
#include "Type.h"
#include "CPos.h"

template< class Key, class DataType >
inline const DataType& GetMapState( const std::map<Key,DataType>& DataMap, Key Pos )
{
	std::map< Key, DataType >::const_iterator next = DataMap.upper_bound( Pos );
	return ( --next )->second;
}

template< class Key, class DataType >
inline DataType GetMapValue( const std::map<Key,DataType>& DataMap, Key Pos )
{
    std::map< Key, DataType >::const_iterator next = DataMap.upper_bound( Pos );
	if( next == DataMap.end() )
		return ( --next )->second;
	std::map< Key, DataType >::const_iterator pre = next;
	--pre;
	float weight = ( next->first - (float)Pos )/( next->first - pre->first );
	return (DataType)( pre->second*weight + next->second*( 1.0f - weight ) );
}

class CRandGen
{
	int32 nRandSeed;

public:
	CRandGen( int32 nSeed )
	{
		nRandSeed = nSeed;
	}

	CRandGen()
	{
		//time_t uOut;
		//nRandSeed = time(&uOut);
		///*
		//_asm
		//{
		//	mov         edx,7FFE0000h 
		//	mov         eax,dword ptr [edx] 
		//	mov			nOut,eax
		//}
		//*/
		//nRandSeed = (int32)uOut;

		nRandSeed=(int32)time(NULL);
	}

	operator int32()
	{
		return (((nRandSeed = nRandSeed * 214013L + 2531011L) >> 16) & 0x7fff);
	}

	int32 operator = ( int32 nSeed )
	{
		return nRandSeed = nSeed;
	}

	int32 GetSeed() const
	{
		return nRandSeed;
	}

	template<class T>
	static T Rand( const T& Min, const T& Max )
	{ 
		static CRandGen sGlob;
		return (T)( sGlob*( Max - Min )/0x8000 + Min ); 
	}
};

template< class M, class V >
inline void CreateShadowMatrixOnPlane( M& mat, const V& vecLightDir, 
							   const V& vecPlaneNormal, const V& posPointOfPlane )
{
	float fDiv = 1.0f/( vecPlaneNormal.Dot( vecLightDir ) );
	float fDot = vecPlaneNormal.Dot( posPointOfPlane );
	mat._11 = (  vecPlaneNormal.y*vecLightDir.y + vecPlaneNormal.z*vecLightDir.z )*fDiv;
	mat._12 = ( -vecPlaneNormal.x*vecLightDir.y )*fDiv;
	mat._13 = ( -vecPlaneNormal.x*vecLightDir.z )*fDiv;
	mat._14 = 0;
	mat._21 = ( -vecPlaneNormal.y*vecLightDir.x )*fDiv;
	mat._22 = (  vecPlaneNormal.x*vecLightDir.x + vecPlaneNormal.z*vecLightDir.z )*fDiv;
	mat._23 = ( -vecPlaneNormal.y*vecLightDir.z )*fDiv;
	mat._24 = 0;
	mat._31 = ( -vecPlaneNormal.z*vecLightDir.x )*fDiv;
	mat._32 = ( -vecPlaneNormal.z*vecLightDir.y )*fDiv;
	mat._33 = (  vecPlaneNormal.x*vecLightDir.x + vecPlaneNormal.y*vecLightDir.y )*fDiv;
	mat._34 = 0;
	mat._41 = ( fDot*vecLightDir.x )*fDiv;
	mat._42 = ( fDot*vecLightDir.y )*fDiv;
	mat._43 = ( fDot*vecLightDir.z )*fDiv;
	mat._44 = 1;
}

template<class T, class ActionClass>
bool ArkTightLine( const TPos<T>& SrcPos, const TPos<T>& DestPos, ActionClass& Action )
{
	int32 xRange = abs( DestPos.x - SrcPos.x );
	int32 yRange = abs( DestPos.y - SrcPos.y );

	int32 xDelta = DestPos.x < SrcPos.x ? -1 : 1;
	int32 yDelta = DestPos.y < SrcPos.y ? -1 : 1;

	TPos<T> CurPos = SrcPos;
	
	if( !Action.Do( CurPos ) )
		return false;

	int32 nCurDx = 0;
	int32 nCurDy = 0;
	int32 nJudge = 0;

	if( yRange > xRange )
	{
		nCurDy = 1;
		nJudge = -( max( yRange, 2 )>>1 );
	}
	else
	{
		nCurDx = 1;
		nJudge = ( xRange>>1 );
	}

	while( CurPos != DestPos )
	{
		if( nJudge < 0 )
		{
			CurPos.y = CurPos.y + yDelta;
			nCurDy++;
			nJudge += xRange;
		}
		else
		{
			CurPos.x = CurPos.x + xDelta;
			nCurDx++;
			nJudge -= yRange;
		}

		if( !Action.Do( CurPos ) )
			return false;
	}
	return true;
}

template<class T, class ActionClass>
bool ArkLine( const TPos<T>& SrcPos, const TPos<T>& DestPos, ActionClass& Action )
{
	int32 xRange = abs( DestPos.x - SrcPos.x );
	int32 yRange = abs( DestPos.y - SrcPos.y );

	int32 xDelta = DestPos.x < SrcPos.x ? -1 : 1;
	int32 yDelta = DestPos.y < SrcPos.y ? -1 : 1;

	TPos<T> CurPos = SrcPos;

	if( !Action.Do( CurPos ) )
		return false;

	int nJudge = 0;

	if( yRange > xRange )
	{
		nJudge = -( yRange>>1 );
		while( CurPos.y != DestPos.y )
		{
			if( nJudge > 0 )
			{
				CurPos.x += xDelta;
				nJudge -= yRange;
			}

			CurPos.y += yDelta;
			nJudge += xRange;

			if( !Action.Do( CurPos ) )
				return false;
		}
	}
	else
	{
		nJudge = ( xRange>>1 );
		while( CurPos.x != DestPos.x )
		{
			if( nJudge < 0 )
			{
				CurPos.y += yDelta;
				nJudge += xRange;
			}

			CurPos.x += xDelta;
			nJudge -= yRange;

			if( !Action.Do( CurPos ) )
				return false;
		}
	}

	return true;
}
